/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Controle;

import Estruturas.Pixel;
import Matriz.ManipulacaoMatriz;
import java.awt.Point;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.renderable.ParameterBlock;
import java.util.Arrays;
import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;
import javax.media.jai.Histogram;
import javax.media.jai.JAI;
import javax.media.jai.KernelJAI;
import javax.media.jai.PlanarImage;
import javax.media.jai.RenderedOp;
import javax.media.jai.TiledImage;

/**
 *
 * @author Linton
 */
public class ManipularImagem {

    public static PlanarImage equalizarImagem(PlanarImage source) {

        int[] histograma = gerarHistograma(source);//HistogramPlot.getHistogram(source);//histograma da imagem
        double[] histogramaNormalizado = new double[histograma.length];//histograma normalizado
        double[] pixels;
        int height = source.getHeight();
        int width = source.getWidth();
        int dim = height * width;

        histogramaNormalizado[0] = histograma[0] / dim;
        for (int i = 1; i < histograma.length; i++) {
            histograma[i] = histograma[i] + histograma[i - 1];// Fazendo o histograma acumulado
            histogramaNormalizado[i] = (double) histograma[i] / dim;//Fazendo o histograma normalizado
        }

        /*Processo de construção da nova imagem*/

        SampleModel sm = source.getSampleModel();
        int nbands = sm.getNumBands();

        Raster inputRaster = source.getData();
        ColorModel c = source.getColorModel();
        int pixelSize = (c.getPixelSize() / nbands);
        WritableRaster outputRaster = inputRaster.createCompatibleWritableRaster();
        pixels = new double[nbands * width * height];//pixels alterados

        /*Fazendo vetor de pixels da imagem original*/
        int[] pixelsImagemOriginal = new int[nbands * height * width];
        inputRaster.getPixels(0, 0, width, height, pixelsImagemOriginal);


        /*construindo novo vetor de pixels equalizados*/
        for (int i = 0; i < dim; i++) {
            pixels[i] = (double) histogramaNormalizado[pixelsImagemOriginal[i]] * (Math.pow(2, pixelSize) - 1);
        }

        outputRaster.setPixels(0, 0, width, height, pixels);

        TiledImage ti = new TiledImage(source, width, height);

        ti.setData(outputRaster);

        return ti;
    }

    public static int[] gerarHistograma(PlanarImage image) {

        int numBits = image.getColorModel().getPixelSize();
        int numCanais = image.getColorModel().getNumComponents();
        int numBitsPorPixel = numBits / numCanais;
        double pixelComponent = Math.pow(2, numBitsPorPixel);

        int[] bins = {(int) pixelComponent};
        double[] low = {0.0D};
        double[] high = {pixelComponent};

        ParameterBlock pb = new ParameterBlock();
        pb.addSource(image);
        pb.add(null);
        pb.add(1);
        pb.add(1);
        pb.add(bins);
        pb.add(low);
        pb.add(high);

        RenderedOp op = JAI.create("histogram", pb, null);
        Histogram histogram = (Histogram) op.getProperty("histogram");

        // captura conteudo do histograma
        int[] local_array = new int[histogram.getNumBins(0)];
        for (int i = 0; i < histogram.getNumBins(0); i++) {
            local_array[i] = histogram.getBinSize(0, i);
        }

        return local_array;
    }

    public static PlanarImage binarizarImagem(PlanarImage source, int escala) {
        int height = source.getHeight();
        int width = source.getWidth();
        PlanarImage output = null;
        int limiar = 0;
        if (escala == -1) {
            limiar = limiarOtimo(source);
        } else {
            limiar = ManipularImagem.getMaximaIntensidadeDaImagem(source) * escala / 100;
        }
        /* Converte o vetor de pixels em matriz */
        Pixel[][] matrizPixels = ManipularImagem.planarToMatrizPixels(source);

        //int max = ManipularImagem.getMaximaIntensidadeDaImagem(source);
        for (int i = 1; i < height - 1; i++) {
            for (int j = 1; j < width - 1; j++) {
                // Se o pixel é brilhante...
                if (!(matrizPixels[i][j].getNivelCinza() > limiar)) {
                    matrizPixels[i][j].setNivelCinza(0);
                } else {
                    matrizPixels[i][j].setNivelCinza(255);
                }
            }
        }

        output = matrizPixelsToPlanar(matrizPixels, height, width, source);
        return output;
    }

    public static PlanarImage convertColorToGray(PlanarImage source) {

        if (source.getNumBands() == 3) {
            double[][] matrix = {{.114D, 0.587D, 0.299D, 0},
                {.114D, 0.587D, 0.299D, 0},
                {.114D, 0.587D, 0.299D, 0}
            };

            if (source != null) {
                ParameterBlock pb = new ParameterBlock();
                pb.addSource(source);
                pb.add(matrix);
                source = JAI.create("bandcombine", pb, null);
            }
        }

        return source;
    }

    //encontra o limiar ótimo da imagem
    public static int limiarOtimo(PlanarImage imagem) {

        int[] bitsPorCor = imagem.getColorModel().getComponentSize();
        int[] hist = new int[(int) Math.pow(2, bitsPorCor[0]) - 1];
        hist = gerarHistograma(imagem);
        int comprimento = imagem.getWidth();
        int largura = imagem.getHeight();
        double quantidadePixels = comprimento * largura;
        double mediaTotal = 0;
        double W0, W1, media0, media1;
        double variQuad = 0;
        double maxVariQuad = 0;
        int limiarOtimo = 0;

        for (int i = 0; i < hist.length; i++) {
            mediaTotal = mediaTotal + i * (hist[i]);///quantidadePixels);//Cálculo de média total
        }

        mediaTotal = mediaTotal / quantidadePixels;

        for (int limiar = 1; limiar < hist.length - 1; limiar++) {
            W0 = W1 = media0 = media1 = 0;

            for (int i = 0; i < limiar; i++) {
                W0 = W0 + hist[i];///quantidadePixels;//Somatório da primeira classe
            }

            W0 = W0 / quantidadePixels;

            for (int j = limiar; j < hist.length; j++) {
                W1 = W1 + hist[j];///quantidadePixels;//Somatório da segunda classe
            }

            W1 = W1 / quantidadePixels;

            for (int i = 0; i < limiar; i++) {
                media0 = media0 + (i * (hist[i]));///quantidadePixels))/W0;//Média da primeira classe
            }

            media0 = media0 / quantidadePixels / W0;

            for (int j = limiar; j < hist.length; j++) {
                media1 = media1 + (j * (hist[j]));///quantidadePixels))/W1;//Média da segunda classe
            }

            media1 = media1 / quantidadePixels / W1;

            variQuad = W0 * (Math.pow((media0 - mediaTotal), 2)) + W1 * (Math.pow((media1 - mediaTotal), 2));//Cálculo da variância ao quadrado

            if (variQuad > maxVariQuad)//Verifica a maximização da variância
            {
                limiarOtimo = limiar;
                maxVariQuad = variQuad;
            }

        }

        return limiarOtimo;

    }

    //converte uma planar imagem em uma matriz de pixels correspondente
    public static int[][] planarToMatriz(PlanarImage source) {
        int[] pixels;
        int[][] matrizPixels, matrizDePontos;
        SampleModel sm = source.getSampleModel();
        int nbands = sm.getNumBands();
        int height = source.getHeight();
        int width = source.getWidth();

        Raster inputRaster = source.getData();

        pixels = new int[nbands * width * height];//pixels alterados

        /*Fazendo vetor de pixels da imagem original*/
        int[] pixelsImagemOriginal = new int[nbands * height * width];

        pixels = inputRaster.getPixels(0, 0, width, height, pixelsImagemOriginal);
        matrizPixels = ManipulacaoMatriz.vetorParaMatriz(height, width, pixels);

        return matrizPixels;
    }

    //converte uma matriz de pixels em uma planar imagem
    public static PlanarImage matrizToPlanar(int[][] matrizDePontos, int height, int width, PlanarImage source) {

        Raster inputRaster = source.getData();
        int[] vetorPontos = ManipulacaoMatriz.matrizParaVetor(matrizDePontos, height, width);
        WritableRaster outputRaster = inputRaster.createCompatibleWritableRaster();
        outputRaster.setPixels(0, 0, width, height, vetorPontos);
        TiledImage ti = new TiledImage(source, width, height);
        ti.setData(outputRaster);
        return ti;

    }

    public List Sort(String[] args) {

        List<String> list = Arrays.asList(args);
        Collections.sort(list);
        System.out.println(list);
        return list;

    }

    public static Pixel[][] planarToMatrizPixels(PlanarImage source) {
        int[] pixels;
        Pixel[][] matrizPixels;
        SampleModel sm = source.getSampleModel();
        int nbands = sm.getNumBands();
        int height = source.getHeight();
        int width = source.getWidth();

        Raster inputRaster = source.getData();

        pixels = new int[nbands * width * height];//pixels alterados

        /*Fazendo vetor de pixels da imagem original*/
        int[] pixelsImagemOriginal = new int[nbands * height * width];

        pixels = inputRaster.getPixels(0, 0, width, height, pixelsImagemOriginal);
        matrizPixels = ManipulacaoMatriz.vetorParaMatrizPixels(height, width, pixels);


        return matrizPixels;
    }

    public static PlanarImage matrizPixelsToPlanar(Pixel[][] matrizDePontos, int height, int width, PlanarImage source) {

        Raster inputRaster = source.getData();
        double[] vetorPontos = ManipulacaoMatriz.matrizPixelsParaVetor(matrizDePontos, height, width);
        WritableRaster outputRaster = inputRaster.createCompatibleWritableRaster();
        outputRaster.setPixels(0, 0, width, height, vetorPontos);
        TiledImage ti = new TiledImage(source, width, height);
        ti.setData(outputRaster);
        return ti;

    }

    /* Este método retorna o máximo nivel de cinza presente na imagem */
    private static int getMaximaIntensidadeDaImagem(PlanarImage source) {

        int max = 0;
        int[] histograma = ManipularImagem.gerarHistograma(source);

        for (int i = histograma.length - 1; i >= 0; i--) {
            max = histograma[i];
            if (max > 0) {
                return i;
            }
        }
        return max;
    }

    public static PlanarImage aplicarFiltroDeMedia(PlanarImage source) {

    float[] kernelMatrix = {
                    1f / 25f, 1f / 25f, 1f / 25f, 1f / 25f, 1f / 25f,
                    1f / 25f, 1f / 25f, 1f / 25f, 1f / 25f, 1f / 25f,
                    1f / 25f, 1f / 25f, 1f / 25f, 1f / 25f, 1f / 25f,
                    1f / 25f, 1f / 25f, 1f / 25f, 1f / 25f, 1f / 25f,
                    1f / 25f, 1f / 25f, 1f / 25f, 1f / 25f, 1f / 25f
        };

        KernelJAI kernel = new KernelJAI(5, 5, kernelMatrix);
        return JAI.create("convolve", source, kernel);
    }

    public static PlanarImage recortar(PlanarImage ri, float x, float y, float altura, float largura) {

        // Recorta a imagem...
        ParameterBlock pb = new ParameterBlock();
        pb.addSource(ri);
        pb.add(x);
        pb.add(y);
        pb.add(largura);
        pb.add(altura);
        ri = JAI.create("crop", pb, null);

        // Reposiciona a imagem recortada...
        ParameterBlock pbAux = new ParameterBlock();
        pbAux.addSource(ri);
        pbAux.add(-x);
        pbAux.add(-y);

        PlanarImage recortadaOK = JAI.create("translate", pbAux, null);

        return recortadaOK;
    }
}
